package org.rujue.common.utils;

import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

/**
 * Spring应用上下文持有者 (线程安全版)
 *
 * <p>提供全局访问Spring容器的能力，适用于以下场景：</p>
 * <ul>
 *   <li>静态工具类中需要获取Bean</li>
 *   <li>非Spring管理对象需要依赖注入</li>
 *   <li>需要手动获取原型(prototype)作用域的Bean</li>
 * </ul>
 *
 */
@Service
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {

	// 使用volatile保证多线程可见性
	private static volatile ApplicationContext applicationContext = null;

	// 双重检查锁对象
	private static final Object LOCK = new Object();

	private static final Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);
	/**
	 * 获取当前应用上下文 (线程安全)
	 * @throws IllegalStateException 当上下文未初始化时抛出
	 */
	public static ApplicationContext getApplicationContext() {
		if (applicationContext == null) {
			synchronized (LOCK) {
				if (applicationContext == null) {
					assertContextInjected();
				}
			}
		}
		return applicationContext;
	}

	/**
	 * 根据名称获取Bean实例
	 * @param name Bean名称
	 * @return Bean实例
	 * @throws BeansException 当Bean不存在时抛出
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getBean(String name) {
		assertContextInjected();
		return (T) applicationContext.getBean(name);
	}

	public static <T> T getBean(Class<T> requiredType) {
		assertContextInjected();
		return applicationContext.getBean(requiredType);
	}

	/**
	 * 清除应用上下文引用 (主要用于测试环境重置)
	 */
	public static void clearHolder() {
		if (logger.isDebugEnabled()) {
			logger.debug("正在清除Spring上下文: {}", applicationContext);
		}
		applicationContext = null;
	}

	/**
	 * 实现ApplicationContextAware接口的回调方法
	 * @param applicationContext 被注入的应用上下文
	 */
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		logger.debug("正在注入Spring上下文: {}", applicationContext);

		if (SpringContextHolder.applicationContext != null) {
			logger.warn("检测到上下文覆盖！原有上下文: {}", SpringContextHolder.applicationContext);
		}

		// 使用同步块保证原子操作
		synchronized (LOCK) {
			SpringContextHolder.applicationContext = applicationContext;
		}
	}

	@Override
	public void destroy() throws Exception {
		SpringContextHolder.clearHolder();
	}

	/**
	 * 断言上下文已成功注入
	 * @throws IllegalStateException 当上下文未初始化时抛出
	 */
	private static void assertContextInjected() {
		Validate.validState(applicationContext != null,
				"applicationContext属性未初始化，请检查配置文件中SpringContextHolder的定义");
	}
}
